package com.yonyou.controller;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wf.captcha.base.Captcha;
import com.yonyou.exception.ErrorMessageException;
import com.yonyou.model.LoginUser;
import com.yonyou.model.ResultEntity;
import com.yonyou.model.SecScsAuthPermission;
import com.yonyou.service.LoginService;
import com.yonyou.service.SecScsAuthPermissionService;
import com.yonyou.util.LoginCodeEnum;
import com.yonyou.util.LoginProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author wangchu
 * @date 2025-08-10 10:23
 */
@Slf4j
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private LoginProperties loginProperties;
    @Autowired
    private LoginService loginService;
    @Autowired
    private SecScsAuthPermissionService authPermissionService;

    /**
     * 验证码缓存
     */
    private final static Map<String, Map<String, Object>> VERIFY_CODE_CACHE_MAP = new ConcurrentHashMap<>(16);

    @PostConstruct
    private void init() {
        flushThread.start();
    }
    /**
     * 清理验证码缓存线程
     */
    Thread flushThread = new Thread(() -> {
        try {
            while (true) {
                // 休眠到5s
                TimeUnit.MILLISECONDS.sleep(60000 - System.currentTimeMillis() % 1000);
                long now = System.currentTimeMillis();
                if (VERIFY_CODE_CACHE_MAP.size() != 0) {
                    boolean flush = false;
                    Iterator<Map.Entry<String, Map<String, Object>>> it = VERIFY_CODE_CACHE_MAP.entrySet().iterator();
                    while (it.hasNext()) {
                        Map.Entry<String, Map<String, Object>> ey = it.next();
                        long timestamp = Long.parseLong(ey.getValue().get("timestamp").toString());
                        long dist = now - timestamp;
                        if (dist > 120000) {
                            log.info("flush captcha {}, distance{}", ey.getKey(), dist);
                            flush = true;
                            it.remove();
                        }
                    }
                    if (flush) {
                        log.info("flush sl buffer over...");
                    }
                }
            }
        } catch (Exception e) {
            log.error("清理缓冲区线程出现异常！", e);
        }
    });

    private Map<String, Object> getVerifyCode() {
        // 获取运算的结果
        Captcha captcha = loginProperties.getCaptcha();
        String uuid = IdUtil.simpleUUID();
        //当验证码类型为 arithmetic时且长度 >= 2 时，captcha.text()的结果有几率为浮点型
        String captchaValue = captcha.text();
        if (captcha.getCharType() - 1 == LoginCodeEnum.arithmetic.ordinal() && captchaValue.contains(".")) {
            captchaValue = captchaValue.split("\\.")[0];
        }
        // 缓存
        Map<String, Object> captchaMap = new HashMap<>(4);
        captchaMap.put("result", captchaValue);
        captchaMap.put("timestamp", System.currentTimeMillis());
        VERIFY_CODE_CACHE_MAP.put(uuid, captchaMap);

        // 验证码信息
        Map<String, Object> imgResult = new HashMap<String, Object>(3) {{
            put("img", captcha.toBase64());
            put("uuid", uuid);
        }};
        return imgResult;
    }

    @GetMapping("/login.html")
    public ModelAndView loginPage() {
        Map<String, Object> imgResult = getVerifyCode();
        return new ModelAndView("login/page")
                .addObject("captcha", imgResult.get("img").toString())
                .addObject("captchaId", imgResult.get("uuid").toString());
    }

    /**
     * 登录方法
     *
     * @param user    用户登陆对象
     * @param request 请求对象
     */
    @RequestMapping(value = {"/login"})
    public ResultEntity<?> login(LoginUser user, HttpServletRequest request) {
        // 判断验证码
        String captchaId = user.getCaptchaId();
        String captcha = user.getCaptcha();
        if (StrUtil.isBlank(captchaId) || StrUtil.isBlank(captcha)) {
            VERIFY_CODE_CACHE_MAP.remove(captchaId);
            throw new ErrorMessageException("验证码不能为空！");
        }
        Map<String, Object> captchaMap = VERIFY_CODE_CACHE_MAP.get(captchaId);
        if (CollUtil.isEmpty(captchaMap)) {
            VERIFY_CODE_CACHE_MAP.remove(captchaId);
            throw new ErrorMessageException("验证码不正确或已过期！");
        }
        String correctVal = captchaMap.get("result").toString();
        long timestamp = Long.parseLong(captchaMap.get("timestamp").toString());
        if (!correctVal.equals(captcha)) {
            VERIFY_CODE_CACHE_MAP.remove(captchaId);
            throw new ErrorMessageException("验证码不正确或已过期！");
        }
        long now = System.currentTimeMillis();
        long dist = now - timestamp;
        if (dist > 120000) {
            VERIFY_CODE_CACHE_MAP.remove(captchaId);
            throw new ErrorMessageException("验证码不正确或已过期！");
        }
        // 验证码正确，删除缓存，防止被重复使用
        VERIFY_CODE_CACHE_MAP.remove(captchaId);

        if (StrUtil.isBlank(user.getTs())) {
            return ResultEntity.fail("时间戳不能为空！");
        }
        long ts = Long.parseLong(user.getTs());
        // 时间戳，时效5分钟
        if (now - ts > 3000000) {
            return ResultEntity.fail("登陆操作时效性超限！");
        }
        // 登录逻辑
        user = loginService.login(user);

        // 判断如果登陆成功，则向session作用域放入在线用户
        if (Objects.isNull(user)) {
            return ResultEntity.fail("登录失败！");
        }
        log.info("【手动登录】:" + user.toString());
        request.getSession().setAttribute("loginUser", user);
        return ResultEntity.success(user);
    }

    @GetMapping("/main.html")
    public ModelAndView mainPage(HttpServletRequest request) {
        LoginUser loginUser = (LoginUser) request.getSession().getAttribute("loginUser");
        if (Objects.isNull(loginUser)) {
            Map<String, Object> imgResult = getVerifyCode();
            return new ModelAndView("login/page")
                    .addObject("captcha", imgResult.get("img").toString())
                    .addObject("captchaId", imgResult.get("uuid").toString());
        } else {
            return new ModelAndView("main/index");
        }
    }

    @RequestMapping("/welcome.html")
    public ModelAndView mainPage() {
        return new ModelAndView("main/main");
    }

    @GetMapping("/exit")
    public ResultEntity<?> exit(HttpServletRequest request) {
        request.getSession().invalidate();
        return ResultEntity.success();
    }
    /**
     * 获取菜单
     */
    @RequestMapping("/getMenu")
    public ResultEntity<?> searchMenu(HttpServletRequest request) {
        // 获取在线用户
        LoginUser loginUser = (LoginUser) request.getSession().getAttribute("loginUser");
        List<SecScsAuthPermission> authPermissions = authPermissionService.list(
                new LambdaQueryWrapper<SecScsAuthPermission>()
                        .eq(SecScsAuthPermission::getBelongButton, false)
                        .eq(SecScsAuthPermission::getState, true)
                        .orderByAsc(SecScsAuthPermission::getCode)
        );
        // 转换成嵌套格式
        Map<String, SecScsAuthPermission> authPermissionMap = new LinkedHashMap<>();
        authPermissions.forEach(a -> authPermissionMap.put(a.getId(), a));
        // 需要删除的ID
        Set<String> removeIds = new HashSet<>();
        for (Map.Entry<String, SecScsAuthPermission> entry : authPermissionMap.entrySet()) {
            SecScsAuthPermission ap = entry.getValue();
            SecScsAuthPermission pAp = authPermissionMap.get(ap.getPid());
            if (Objects.nonNull(pAp)) {
                pAp.getChildren().add(ap);
                removeIds.add(entry.getKey());
            }
        }
        removeIds.forEach(authPermissionMap::remove);
        return ResultEntity.success(authPermissionMap.values());
    }
}